/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#include "octreescenemanager.h"
#include "octreenode.h"
#include "octree.h"
#include "mesh.h"
#include "resourcemanager.h"
#include "graphic.h"
#include "threadpool.h"
#include "functors.h"

#include <vector>
#include <algorithm>

#ifdef _DEBUG
#ifdef _MSC_VER
   #define new new( _CLIENT_BLOCK, __FILE__, __LINE__)
#endif
#endif // _DEBUG

namespace Engine
{
	/**********************************************************************************************************/
	OctreeSceneManager* OctreeSceneManagerFactory::CreateInstance( const std::string& name)
	{
		return new OctreeSceneManager(name);
	}
	/**********************************************************************************************************/
	OctreeSceneManager::OctreeSceneManager(const std::string& name)
		:SceneManager(name)
	{
		AABB b( Vector3<REAL>(-10000, -10000, -10000),Vector3<REAL>(10000, 10000, 10000 ));
		Init( b, 8 );
	}
	/**********************************************************************************************************/
	OctreeSceneManager::~OctreeSceneManager()
	{
		FreeInputStaticGeom();
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::Init( const AABB& box, int d )
	{
		SetRootSceneNode(RefPtr<SceneNode, WeakPtrExtension>(new OctreeNode( this, "RootSceneNode" )));

		m_octree = RefPtr<Octree>(new Octree(0));

		m_maxDepth = d;

		m_box = box;

		m_octree->SetBBox(box);

		Vector3<REAL> min = box.GetMinimum();

		Vector3<REAL> max = box.GetMaximum();

		m_octree->SetHalfSize(( max - min ) / 2);

	}
	/**********************************************************************************************************/
	void OctreeSceneManager::ResetScene()
	{
		m_vertexData.Delete();
		m_renderables.clear();
		m_vertexDeclaration.Delete();
		FreeInputStaticGeom();
		AABB b( Vector3<REAL>(-10000, -10000, -10000),Vector3<REAL>(10000, 10000, 10000 ));
		Init( b, 8 );
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::FindVisibleObjects(const Camera* camera)
	{
		GetRenderQueue()->Reset();
		GetShadowRenderQueue()->Reset();
		camera->UpdateRenderQueue(GetRenderQueue());

		//prepare static geometry
		std::for_each(m_renderables.begin(), m_renderables.end(), on2nd(m_fn(&StaticGeomRenderable::Lock)));

		//walk the octree, adding all visible Octreenodes nodes to the render queue.
		WalkOctree(camera, GetRenderQueue(),GetShadowRenderQueue(),GetLightList(), m_octree.GetPointer(), false);

		//render static geometry
		RenderablesMap::iterator i = m_renderables.begin(), e = m_renderables.end();
		for(;i != e; ++i)
		{
			i->second->UnLock();
			GetRenderQueue()->AddRenderable(i->second);
		}
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::WalkOctree(const Camera* camera, RenderQueue* queue, RenderQueue* shadowsQueue, const std::vector<LightPtr>& lightList, Octree* octant,bool foundvisible)
	{
		//return immediately if nothing is in the node.
		if ( octant->GetNumNodes() == 0 && !octant->HaveStatic())
			return;

		Visibility v = NONE;

		if(foundvisible)
		{
			v = FULL;
		}
		else if(octant == m_octree.GetPointer())
		{
			v = PARTIAL;
		}
		else
		{
			AABB box;
			octant->GetCullBounds(&box);
			v = camera->GetVisibility(box);
		}


		// if the octant is visible, or if it's the root node...
		if ( v != NONE )
		{

			//Add stuff to be rendered;
			NodeList::iterator it = octant->GetNodesBegin();

			Visibility vis = FULL;

			while ( it != octant->GetNodesEnd() )
			{
				OctreeNode* sn = *it;

				// if this octree is partially visible, manually cull all
				// scene nodes attached directly to this level.

				if ( v == PARTIAL )
					vis = camera->GetVisibility( sn->GetBoundingBox() );

				if ( vis == FULL || vis == PARTIAL)
				{
					sn->AddToRenderQueue(camera, queue,shadowsQueue,lightList, vis);
				}
				++it;
			}

			//Update static geometry for render
			m_walkMutex.Lock();
			octant->UpdateStaticGeom(&m_renderables);
			m_walkMutex.Unlock();


			Octree* child;
			bool childfoundvisible = (v == FULL);

			std::vector<RefPtr<BaseTask> > tasks;
			tasks.reserve(8);

			if ( (child = octant->GetChildren( 0 , 0 , 0 )) != 0 )
			{
				tasks.push_back(RefPtr<BaseTask>(new WalkTask(this,camera, queue,shadowsQueue,lightList, child, childfoundvisible)));
				ThreadPool::GetSingleton().Run(tasks.back());
			}
			if ( (child = octant->GetChildren( 1 , 0 , 0 )) != 0 )
			{
				tasks.push_back(RefPtr<BaseTask>(new WalkTask(this,camera, queue,shadowsQueue,lightList, child, childfoundvisible)));
				ThreadPool::GetSingleton().Run(tasks.back());
			}
			if ( (child = octant->GetChildren( 0 , 1 , 0 )) != 0 )
			{
				tasks.push_back(RefPtr<BaseTask>(new WalkTask(this,camera, queue,shadowsQueue,lightList, child, childfoundvisible)));
				ThreadPool::GetSingleton().Run(tasks.back());
			}
			if ( (child = octant->GetChildren( 1 , 1 , 0 )) != 0 )
			{
				tasks.push_back(RefPtr<BaseTask>(new WalkTask(this,camera, queue,shadowsQueue,lightList, child, childfoundvisible)));
				ThreadPool::GetSingleton().Run(tasks.back());
			}
			if ( (child = octant->GetChildren( 0 , 0 , 1 )) != 0 )
			{
				tasks.push_back(RefPtr<BaseTask>(new WalkTask(this,camera, queue,shadowsQueue,lightList, child, childfoundvisible)));
				ThreadPool::GetSingleton().Run(tasks.back());
			}
			if ( (child = octant->GetChildren( 1 , 0 , 1 )) != 0 )
			{
				tasks.push_back(RefPtr<BaseTask>(new WalkTask(this,camera, queue,shadowsQueue,lightList, child, childfoundvisible)));
				ThreadPool::GetSingleton().Run(tasks.back());
			}
			if ( (child = octant->GetChildren( 0 , 1 , 1 )) != 0 )
			{
				tasks.push_back(RefPtr<BaseTask>(new WalkTask(this,camera, queue,shadowsQueue,lightList, child, childfoundvisible)));
				ThreadPool::GetSingleton().Run(tasks.back());
			}
			if ( (child = octant->GetChildren( 1 , 1 , 1 )) != 0 )
			{
				tasks.push_back(RefPtr<BaseTask>(new WalkTask(this,camera, queue,shadowsQueue,lightList, child, childfoundvisible)));
				ThreadPool::GetSingleton().Run(tasks.back());
			}

			//wait child tasks
			for_each(tasks.begin(), tasks.end(), m_fn(&BaseTask::Wait));
		}
		else
		{
			NodeList::iterator it = octant->GetNodesBegin();
		}
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::UpdateOctreeNode(OctreeNode* onode)
	{
		// Skip if octree has been destroyed (shutdown conditions)
		if (m_octree.IsNull())
			return;

		const AABB& box = onode->GetBoundingBox();

		if ( box.IsNull() )
			return ;

		if (onode->GetOctant() == 0)
		{
			//if outside the octree, force into the root node.
			if (!onode->IsIn( m_octree->GetBBox()))
				m_octree->AddNode(onode);
			else
				AddOctreeNode(onode, m_octree.GetPointer());
			return;
		}

		if (!onode->IsIn(onode->GetOctant()->GetBBox()))
		{
			RemoveOctreeNode(onode);

			//if outside the octree, force into the root node.
			if (!onode->IsIn( m_octree->GetBBox()))
				m_octree->AddNode(onode);
			else
				AddOctreeNode(onode, m_octree.GetPointer());
		}
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::AddOctreeNode( OctreeNode* n, Octree* octant, int depth )
	{

		// Skip if octree has been destroyed (shutdown conditions)
		if (m_octree.IsNull())
			return;

		const AABB& bx = n->GetBoundingBox();


		//if the octree is twice as big as the scene node,
		//we will add it to a child.
		if ((depth < m_maxDepth) && octant->IsTwiceSize(bx))
		{
			int x, y, z;
			octant->GetChildIndexes( bx, &x, &y, &z );

			if (octant->GetChildren( x , y , z ) == 0 )
			{
				octant->SetChildren( x , y , z, new Octree( octant ));
				const Vector3<REAL>& octantMin = octant->GetBBox().GetMinimum();
				const Vector3<REAL>& octantMax = octant->GetBBox().GetMaximum();
				Vector3<REAL> min, max;

				if(x == 0)
				{
					min.x = octantMin.x;
					max.x = ( octantMin.x + octantMax.x ) / 2;
				}
				else
				{
					min.x = ( octantMin.x + octantMax.x ) / 2;
					max.x = octantMax.x;
				}

				if (y == 0)
				{
					min.y = octantMin.y;
					max.y = ( octantMin.y + octantMax.y ) / 2;
				}
				else
				{
					min.y = ( octantMin.y + octantMax.y ) / 2;
					max.y = octantMax.y;
				}

				if (z == 0)
				{
					min.z = octantMin.z;
					max.z = ( octantMin.z + octantMax.z ) / 2;
				}
				else
				{
					min.z = ( octantMin.z + octantMax.z ) / 2;
					max.z = octantMax.z;
				}

				octant->GetChildren( x , y , z )->SetBBox(AABB( min, max ));
				octant->GetChildren( x , y , z )->SetHalfSize(( max - min ) / 2);
			}

			AddOctreeNode( n, octant->GetChildren( x , y , z ), ++depth );
		}
		else
		{
			octant->AddNode(n);
		}
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::RemoveOctreeNode( OctreeNode* n )
	{
		// Skip if octree has been destroyed (shutdown conditions)
		if (m_octree.IsNull())
			return;

		Octree* oct = n->GetOctant();

		if (oct != 0)
		{
			oct->RemoveNode(n);
		}

		n->SetOctant(0);
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::AddStaticGeometry(const std::string& fname, const Matrix4<REAL>& transform)
	{
		MeshPtr mesh;
		WeakUtils::UnsafeCopy(mesh, ResourceManager::GetSingleton().Create(fname, "Mesh"));
		mesh->Load();
		m_inputStaticGeom.push_back(MeshVector::value_type(mesh,transform));
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::FreeInputStaticGeom()
	{
		MeshVector::iterator i = m_inputStaticGeom.begin(), e = m_inputStaticGeom.end();
		for(;i != e; ++i)
		{
			if(!i->first.IsNull())
			{
				std::string name = i->first->GetName();
				i->first.Delete();
				ResourceManager::GetSingleton().TryRemove(name);
			}
		}
		m_inputStaticGeom.clear();
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::CopyVertexData(void* destp,unsigned int destOffset,const VertexElement* destElem,const void* srcp,unsigned int srcOffset,const VertexElement* srcElem, const Matrix4<REAL>& transform)
	{
		char* dest = reinterpret_cast<char*>(destp) + destOffset;
		const char* src = reinterpret_cast<const char*>(srcp) + srcOffset;
		switch (destElem->GetType())
		{
		case VET_FLOAT1:
			if(srcElem->GetType() == VET_FLOAT1)
				memcpy(dest,src,destElem->GetSize());
			break;
		case VET_FLOAT2:
			if(srcElem->GetType() == VET_FLOAT1)
				memcpy(dest,src,srcElem->GetSize());
			else if(srcElem->GetType() == VET_FLOAT2)
				memcpy(dest,src,destElem->GetSize());
			break;
		case VET_FLOAT3:
			if(srcElem->GetType() == VET_FLOAT1 || srcElem->GetType() == VET_FLOAT2)
				memcpy(dest,src,srcElem->GetSize());
			else if(srcElem->GetType() == VET_FLOAT3)
			{
				if(destElem->GetSemantic() == VES_POSITION)
				{
					//transform
					float coords[3];
					memcpy(coords,src,destElem->GetSize());
					Vector3<REAL> pos(coords[0],coords[1],coords[2]);
					pos = transform * pos;
					coords[0] = static_cast<float>(pos.x);
					coords[1] = static_cast<float>(pos.y);
					coords[2] = static_cast<float>(pos.z);
					memcpy(dest,coords,destElem->GetSize());
				}
				else
				{
					memcpy(dest,src,destElem->GetSize());
				}
			}
			break;
		case VET_FLOAT4:
			if(srcElem->GetType() == VET_FLOAT1 || srcElem->GetType() == VET_FLOAT2 || srcElem->GetType() == VET_FLOAT3)
				memcpy(dest,src,srcElem->GetSize());
			else if(srcElem->GetType() == VET_FLOAT4)
				memcpy(dest,src,destElem->GetSize());
			break;
		case VET_SHORT1:
			if(srcElem->GetType() == VET_SHORT1)
				memcpy(dest,src,destElem->GetSize());
			break;
		case VET_SHORT2:
			if(srcElem->GetType() == VET_SHORT1)
				memcpy(dest,src,srcElem->GetSize());
			else if(srcElem->GetType() == VET_SHORT2)
				memcpy(dest,src,destElem->GetSize());
			break;
		case VET_SHORT3:
			if(srcElem->GetType() == VET_SHORT1 || srcElem->GetType() == VET_SHORT2)
				memcpy(dest,src,srcElem->GetSize());
			else if(srcElem->GetType() == VET_SHORT3)
				memcpy(dest,src,destElem->GetSize());
			break;
		case VET_SHORT4:
			if(srcElem->GetType() == VET_SHORT1 || srcElem->GetType() == VET_SHORT2 || srcElem->GetType() == VET_SHORT3)
				memcpy(dest,src,srcElem->GetSize());
			else if(srcElem->GetType() == VET_SHORT4)
				memcpy(dest,src,destElem->GetSize());
			break;
		case VET_UBYTE4:
			if(srcElem->GetType() == VET_UBYTE4)
				memcpy(dest,src,destElem->GetSize());
			break;
		case VET_COLOR:
		case VET_COLOR_ARGB:
		case VET_COLOR_ABGR:
			if(srcElem->GetType() == VET_COLOR || srcElem->GetType() == VET_COLOR_ARGB || srcElem->GetType() == VET_COLOR_ABGR)
				memcpy(dest,src,destElem->GetSize());
			break;
		}
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::BuildStaticGeometry()
	{
		MeshVector::iterator i = m_inputStaticGeom.begin(), e = m_inputStaticGeom.end();
		//create common vertex declaration
		m_vertexDeclaration = Graphic::GetSingleton().GetRenderSystem()->CreateVertexDeclaration();
		for(;i != e; ++i)
		{
			VertexDeclarationPtr vdecl =  i->first->GetVertexDeclaration();
			unsigned int count = vdecl->GetElementCount();
			for(unsigned short elem = 0; elem < count; ++elem)
			{
				if(const VertexElement* velem = m_vertexDeclaration->FindElementBySemantic(vdecl->GetElement(elem)->GetSemantic(), vdecl->GetElement(elem)->GetIndex()))
				{
					if(velem->GetType() != vdecl->GetElement(elem)->GetType())
					{
						VertexElementType newType = vdecl->GetElement(elem)->GetType();
						switch (velem->GetType())
						{
						case VET_FLOAT1:
							if(newType == VET_FLOAT2 || newType == VET_FLOAT3 || newType ==  VET_FLOAT4)
								m_vertexDeclaration->AddElement(velem->GetSource(),velem->GetOffset(),newType,velem->GetSemantic(),velem->GetIndex());
							break;
						case VET_FLOAT2:
							if(newType == VET_FLOAT3 || newType ==  VET_FLOAT4)
								m_vertexDeclaration->AddElement(velem->GetSource(),velem->GetOffset(),newType,velem->GetSemantic(),velem->GetIndex());
							break;
						case VET_FLOAT3:
							if(newType ==  VET_FLOAT4)
								m_vertexDeclaration->AddElement(velem->GetSource(),velem->GetOffset(),newType,velem->GetSemantic(),velem->GetIndex());
							break;
						case VET_SHORT1:
							if(newType == VET_SHORT2 || newType == VET_SHORT3 || newType ==  VET_SHORT4)
								m_vertexDeclaration->AddElement(velem->GetSource(),velem->GetOffset(),newType,velem->GetSemantic(),velem->GetIndex());
							break;
						case VET_SHORT2:
							if(newType == VET_SHORT3 || newType ==  VET_SHORT4)
								m_vertexDeclaration->AddElement(velem->GetSource(),velem->GetOffset(),newType,velem->GetSemantic(),velem->GetIndex());
							break;
						case VET_SHORT3:
							if(newType ==  VET_SHORT4)
								m_vertexDeclaration->AddElement(velem->GetSource(),velem->GetOffset(),newType,velem->GetSemantic(),velem->GetIndex());
							break;
						case VET_COLOR_ARGB:
						case VET_COLOR_ABGR:
							if(newType ==  VET_SHORT4)
								m_vertexDeclaration->AddElement(velem->GetSource(),velem->GetOffset(),VET_COLOR,velem->GetSemantic(),velem->GetIndex());
							break;
						case VET_FLOAT4:
						case VET_COLOR:
						case VET_SHORT4:
						case VET_UBYTE4:
							break;
						}
						m_vertexDeclaration->RemoveElement(velem);
					}
				}
				else
				{
					m_vertexDeclaration->AddElement(0,0,vdecl->GetElement(elem)->GetType(),vdecl->GetElement(elem)->GetSemantic(),vdecl->GetElement(elem)->GetIndex());
				}
			}
		}
		//update common vertex declaration offsets
		m_vertexDeclaration->UpdateOffsets();

		//create common vertex buffer
		unsigned int vertexCount = 0;
		std::map<std::string, int> materialFaces;
		MeshVector::iterator m = m_inputStaticGeom.begin(), me = m_inputStaticGeom.end();
		for(;m != me; ++m)
			vertexCount += m->first->GetVertexCount();
		
		if(vertexCount > 0)
		{
			m_vertexData = Graphic::GetSingleton().GetRenderSystem()->CreateVertexBuffer(vertexCount, m_vertexDeclaration->GetVertexSize(0));

			//fill common vertex buffer
			void* vertexDataPtr = m_vertexData->Lock();
			m = m_inputStaticGeom.begin(), me = m_inputStaticGeom.end();
			unsigned short indicesOffset = 0;
			unsigned int verticesOffset = 0;

			for(;m != me; ++m)
			{
				VertexDeclarationPtr vdecl = m->first->GetVertexDeclaration();
				size_t count = vdecl->GetElementCount();
				const void* meshVertexDataPtr = m->first->GetVertexData()->Lock();
				//vertex loop
				for(unsigned int vert = 0; vert < m->first->GetVertexCount(); ++vert)
				{
					//vertex elements loop
					for(unsigned short elem = 0; elem < count; ++elem)
					{
						const VertexElement* velem = m_vertexDeclaration->FindElementBySemantic(vdecl->GetElement(elem)->GetSemantic(),vdecl->GetElement(elem)->GetIndex());
						CopyVertexData(vertexDataPtr,verticesOffset + velem->GetOffset(),velem,meshVertexDataPtr,vert * vdecl->GetVertexSize(0) + vdecl->GetElement(elem)->GetOffset(),vdecl->GetElement(elem),m->second);
					}
					verticesOffset += m_vertexDeclaration->GetVertexSize(0);
				}

				//add indices to octree nodes
				const VertexElement* velem = m_vertexDeclaration->FindElementBySemantic(VES_POSITION);
				if(velem->GetType() == VET_FLOAT3)
				{
					for(unsigned int sm = 0; sm  < m->first->GetNumSubMeshes(); ++sm)
					{
						SubMeshPtr smesh = m->first->GetSubMesh(sm);
						unsigned int faceCount = smesh->GetFaceCount();
						materialFaces[smesh->GetMaterial()->GetName()] += faceCount;
						const unsigned short* indexDataPtr = reinterpret_cast<const unsigned short*>(smesh->GetIndexData()->Lock());
						for(unsigned int fi = 0; fi < faceCount; ++fi)
						{
							Triangle tri;
							float coords[3] = {};
							memcpy(coords,reinterpret_cast<char*>(vertexDataPtr) + indicesOffset + indexDataPtr[fi * 3 + 0] * m_vertexDeclaration->GetVertexSize(0) + velem->GetOffset(), sizeof(coords));
							tri.index0 = indicesOffset + indexDataPtr[fi * 3 + 0],tri.v0.x = coords[0],tri.v0.y = coords[1],tri.v0.z = coords[2];
							memcpy(coords,reinterpret_cast<char*>(vertexDataPtr) + indicesOffset + indexDataPtr[fi * 3 + 1] * m_vertexDeclaration->GetVertexSize(0) + velem->GetOffset(), sizeof(coords));
							tri.index1 = indicesOffset + indexDataPtr[fi * 3 + 1],tri.v1.x = coords[0],tri.v1.y = coords[1],tri.v1.z = coords[2];
							memcpy(coords,reinterpret_cast<char*>(vertexDataPtr) + indicesOffset + indexDataPtr[fi * 3 + 2] * m_vertexDeclaration->GetVertexSize(0) + velem->GetOffset(), sizeof(coords));
							tri.index2 = indicesOffset + indexDataPtr[fi * 3 + 2],tri.v2.x = coords[0],tri.v2.y = coords[1],tri.v2.z = coords[2];
							tri.material = smesh->GetMaterial()->GetName();
							AddTriangle(tri, m_octree.GetPointer());
						}
						smesh->GetIndexData()->UnLock();
					}
				}
				indicesOffset = indicesOffset + static_cast<unsigned short>(m->first->GetVertexCount());
			}
			m_vertexData->UnLock();

			//create renderables
			std::map<std::string, int>::iterator ri = materialFaces.begin(), re = materialFaces.end();
			m_renderables.clear();
			for(;ri != re; ++ri)
			{
				MaterialPtr mat;
				WeakUtils::UnsafeCopy(mat, ResourceManager::GetSingleton().Create(ri->first,"Material"));
				m_renderables[ri->first] = StaticGeomRenderablePtr(new StaticGeomRenderable(m_vertexDeclaration, m_vertexData, mat, ri->second, vertexCount));
			}
		}
		else
		{
			m_vertexData.Delete();
			m_renderables.clear();
			m_vertexDeclaration.Delete();
		}
		FreeInputStaticGeom();
	}
	/**********************************************************************************************************/
	void OctreeSceneManager::AddTriangle( const Triangle& tri, Octree* octant, int depth )
	{

		// Skip if octree has been destroyed (shutdown conditions)
		if (m_octree.IsNull())
			return;

		AABB bx;
		bx.Reset();
		bx.Merge(tri.v0);
		bx.Merge(tri.v1);
		bx.Merge(tri.v2);


		//if the octree is twice as big as the scene node,
		//we will add it to a child.
		if ((depth < m_maxDepth) && octant->IsTwiceSize(bx))
		{
			int x, y, z;
			octant->GetChildIndexes( bx, &x, &y, &z );

			if (octant->GetChildren( x , y , z ) == 0 )
			{
				octant->SetHaveStatic(true);
				octant->SetChildren( x , y , z, new Octree( octant , true));
				const Vector3<REAL>& octantMin = octant->GetBBox().GetMinimum();
				const Vector3<REAL>& octantMax = octant->GetBBox().GetMaximum();
				Vector3<REAL> min, max;

				if(x == 0)
				{
					min.x = octantMin.x;
					max.x = ( octantMin.x + octantMax.x ) / 2;
				}
				else
				{
					min.x = ( octantMin.x + octantMax.x ) / 2;
					max.x = octantMax.x;
				}

				if (y == 0)
				{
					min.y = octantMin.y;
					max.y = ( octantMin.y + octantMax.y ) / 2;
				}
				else
				{
					min.y = ( octantMin.y + octantMax.y ) / 2;
					max.y = octantMax.y;
				}

				if (z == 0)
				{
					min.z = octantMin.z;
					max.z = ( octantMin.z + octantMax.z ) / 2;
				}
				else
				{
					min.z = ( octantMin.z + octantMax.z ) / 2;
					max.z = octantMax.z;
				}

				octant->GetChildren( x , y , z )->SetBBox(AABB( min, max ));
				octant->GetChildren( x , y , z )->SetHalfSize(( max - min ) / 2);
			}

			AddTriangle( tri, octant->GetChildren( x , y , z ), ++depth );
		}
		else
		{
			octant->AddTriangle(tri);
		}
	}
	/**********************************************************************************************************/

	/**********************************************************************************************************/
	StaticGeomRenderable::StaticGeomRenderable(VertexDeclarationPtr vertexDeclaration, HardWareBufferPtr vertexData, MaterialPtr material,unsigned int faces, unsigned int vertices)
		:m_indexDataPtr(0)
		,m_index(0)
		,m_transform(Matrix4<REAL>::IDENTITY)
		,m_material(material)
		,m_vertexDeclaration(vertexDeclaration)
		,m_vertexData(vertexData)

	{
		m_indexData = Graphic::GetSingleton().GetRenderSystem()->CreateIndexBuffer(faces, INDEX_BUFFER_16);
		m_renderData = Graphic::GetSingleton().GetRenderSystem()->CreateRenderData(faces, vertices, m_vertexDeclaration,m_indexData,m_vertexData);
	}
	/**********************************************************************************************************/
	StaticGeomRenderable::~StaticGeomRenderable()
	{
		if(!m_material.IsNull())
		{
			std::string name = m_material->GetName();
			m_material.Delete();
			ResourceManager::GetSingleton().TryRemove(name);
		}
	}
	/**********************************************************************************************************/
	void StaticGeomRenderable::Lock()
	{
		m_indexDataPtr = reinterpret_cast<unsigned short*>(m_indexData->Lock());
		if(m_indexDataPtr == NULL)
			throw std::logic_error("Can't lock index buffer");
		m_index = 0;
	}
	/**********************************************************************************************************/
	void StaticGeomRenderable::UnLock()
	{
		m_indexData->UnLock();
		m_indexDataPtr = 0;

		if(m_index != 0)
			m_renderData->SetPrimitiveCount(m_index / 3);
		else
			m_renderData->SetPrimitiveCount(0);

		m_index = 0;
	}
	/**********************************************************************************************************/
	void StaticGeomRenderable::AddIndex(const unsigned short* v, size_t len)
	{
		if(m_indexDataPtr != NULL)
		{
			memcpy(m_indexDataPtr + m_index, v, len * sizeof(unsigned short));
			m_index += len;
		}
		else
			throw std::logic_error("Not initialized index buffer.");
	}
	/**********************************************************************************************************/
	const MaterialPtr StaticGeomRenderable::GetMaterial(void) const
	{
		return m_material;
	}
	/**********************************************************************************************************/
	RenderDataPtr StaticGeomRenderable::GetRenderData() const
	{
		return m_renderData;
	}
	/**********************************************************************************************************/
	const Matrix4<REAL>& StaticGeomRenderable::GetWorldTransform() const
	{
		return m_transform;
	}
	/**********************************************************************************************************/
	const HardWareBufferPtr StaticGeomRenderable::GetVertexData() const
	{
		return m_vertexData;
	}
	/**********************************************************************************************************/
	const HardWareBufferPtr StaticGeomRenderable::GetIndexData() const
	{
		return m_indexData;
	}
	/**********************************************************************************************************/
	const VertexDeclarationPtr StaticGeomRenderable::GetVertexDeclaration() const
	{
		return m_vertexDeclaration;
	}
	/**********************************************************************************************************/
}

